//==============================================================================
//
//          DScore - the score datatype in the ofc-library
//
//               Copyright (C) 2005  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2005-05-05 07:12:19 $ $Revision: 1.2 $
//
//==============================================================================

#include "ofc/DScore.h"

#include <math.h>


#if _INTERFACE_

#include "ofc/config.h"


//
// The DScore class implements a score in a (discrete) distribution. The score
// object is updated with new values and calculates the length, the average or 
// mean, the variance, the standard deviation, the sum and the squared sum. A 
// score object is not a collection: the update values are not stored in the
// object; the state of the object is updated with the new value.
// 

@interface DScore : Object
{
@private
  long                _length;     // the number of values in the object
  double              _min;        // the minimum value of the range
  double              _max;        // the maximum value of the range
  double              _sum;        // the sum of the values
  double              _sumSquared; // the sum of the squared values
  double              _percentage; // the percentage of this score in the distribution (0.0 .. 100.0)
}

#endif



@implementation DScore

  
//// Constructors

//
// Initialise the object with no range
//
// @return the object
//
- (DScore *) init
{
  [super init];

  _min = 0.0;
  _max = 0.0;
  
  [self reset];
  
  return self;
}

//
// Initialise with a range
//
// @param min      the minimal value of the range
// @param max      the maximum value of the range
// 
// @return the object
//
- (DScore *) init :(double) min :(double) max
{
  [self init];

  [self range :min :max];
  
  return self;
}

//
// Initialise with a discrete value
//
// @param value    the discrete value
// 
// @return the object
//
- (DScore *) init :(int) value
{
  [self init];

  [self range :value];
  
  return self;
}


//// Member methods

//
// Return the mininal value
// 
// @return the minimal value
//
- (double) min
{
  return _min;
}

//
// Return the maximum value
// 
// @return the maximum value
// 
- (double) max
{
  return _max;
}

//
// Return the number of the values
// 
// @return the number
// 
- (int) length
{
  return _length;
}

//
// Return the sum of the values
// 
// @return the sum
//
- (double) sum
{
  return _sum;
}

//
// Return the sum of the squared values
// 
// @return the sum of the squared values
// 
- (double) sumSquared
{
  return _sumSquared;
}

//
// Return the percentage of this score in the full distribution
// 
// @return the percentage (0.0 .. 100.0)
// 
- (double) percentage
{
  return _percentage;
}


//// Set methods

//
// Set the range (only when the length is zero)
//
// @param min      the minimal value
// @param max      the maximal value
//
// @return success
//
- (BOOL) range :(double) min :(double) max
{
  BOOL ok = NO;
  
  if (_length > 0)
  {
    WARNING(DW_MEMBER_ALREADY_SET, "min/max");
  }
  else
  {
    if (max > min)
    {
      _max = max;
      _min = min;
    }
    else
    {
      _min = max;
      _max = min;
    }
    
    ok = YES;
  }
  
  return ok;
}

//
// Set the range with a discrete value (only when the length is zero)
//
// @param value    the discrete value
//
// @return success
//
- (BOOL) range :(int) value
{
  return [self range :(double) value-0.5 :(double) value+0.5];
}

//
// Reset the length and sums to zero
// 
// @return the object
// 
- (DScore *) reset
{
  _length     = 0;
  _sum        = 0.0;
  _sumSquared = 0.0;
  _percentage = 100.0;
  
  return self;
}

//
// Set the number of the values in the distribution
// 
// @param length   the number of values in the distribution
// 
// @return the object
// 
- (DScore *) distribution :(long) length
{
  if ((length > 0) && (length >= _length))
  {
    _percentage = (double) _length / (double) length * 100.0;
  }
  else
  {
    _percentage = 100.0;
  }
  return self;
}

//
// Update the object with a value
// 
// @param value    the update value
// 
// @return success
// 
- (BOOL) update :(double) value
{
  BOOL ok = NO;
  
  if ((value < _min) || (value > _max))
  {
    WARNING(DW_INVALID_ARG, "value");
  }
  else
  {
    _length     ++;
    _sum        += value;
    _sumSquared += (value * value);
    
    ok = YES;
  }

  return ok;
}


//// Calculation methods

//
// Calculate the mean of the values
// 
// @return the mean value
// 
- (double) mean
{
  return [self average];
}

//
// Calculate the average of the values
// 
// @return the average value
// 
- (double) average
{
  if (_length > 0)
  {
    return _sum / (double) _length;
  }
  else
  {
    return 0.0;
  }
}

//
// Calculate the variance of the values
// 
// @return the variance
// 
- (double) variance
{
  if (_length > 0)
  {
    double mean = [self average];
    
    return (_sumSquared - 2.0 * mean * _sum + (double) _length * mean * mean) / (double) _length;
  }
  else
  {
    return 0.0;
  }
}

//
// Calculate the standard deviation of the values
// 
// @return the standard deviation
// 
- (double) standardDeviation
{
  return sqrt([self variance]);
}

@end

/*===========================================================================*/
